package com.naiterui.ehp.bs.im.service.message.handler;

import com.naiterui.common.redis.RedisUtil;
import com.naiterui.ehp.bp.domain.PatientInquirer;
import com.naiterui.ehp.bp.utils.thread.ThreadPoolUtil;
import com.naiterui.ehp.bs.im.feign.DoctorFeginClient;
import com.naiterui.ehp.bs.im.repository.PatientInquirerRepository;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.naiterui.ehp.bp.bo.patient.MsgMaPublishParamBO;
import com.naiterui.ehp.bp.constants.CommonConstant;
import com.naiterui.ehp.bp.constants.ImConstants;
import com.naiterui.ehp.bs.im.config.MediaFileConfig;
import com.naiterui.ehp.bs.im.exception.NaiteruiIMException;
import com.naiterui.ehp.bs.im.feign.PatientFeginClient;
import com.naiterui.ehp.bs.im.network.mqtt.handler.ClientStatusHandler;
import com.naiterui.ehp.bs.im.network.protocl.DoctorsMedicalRecord;
import com.naiterui.ehp.bs.im.network.protocl.Media;
import com.naiterui.ehp.bs.im.network.protocl.Message;
import com.naiterui.ehp.bs.im.network.protocl.RecommandDrug;
import com.naiterui.ehp.bs.im.network.protocl.Session;
import com.naiterui.ehp.bs.im.network.protocl.SessionDetails;
import com.naiterui.ehp.bs.im.network.transcode.IMessageTransCoder;
import com.naiterui.ehp.bs.im.network.transfer.DoctorTransfer;
import com.naiterui.ehp.bs.im.repository.mongo.MessageDao;
import com.naiterui.ehp.bs.im.service.message.push.PushHandler;
import com.naiterui.ehp.bs.im.utils.Constants;
import com.naiterui.ehp.bs.im.utils.NameUtils;

import cn.hutool.json.JSONUtil;

/**
 * 医生端消息处理类
 */
@Service
public class DoctorMessageHandler extends AbstractMessageHandler {

    /**
     * LOGGER
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(DoctorMessageHandler.class);

    private PatientInquirerRepository patientInquirerRepository;

    /**
     * 消息数据持久化对象
     */
    private final MessageDao messageDao;

    /**
     * push推送处理
     */
    private final PushHandler pushHandler;

    /**
     * 客户端在线状态处理类：<code>clientHandler</code>
     */
    private final ClientStatusHandler clientHandler;

    /**
     * Session处理类：<code>sessionHandler</code>
     */
    private final SessionHandler sessionHandler;

    /**
     * 向客户端发送消息的传输对象
     */
    private final DoctorTransfer messageTransfer;

    private final MediaFileConfig mediaConfig;

    private final DoctorFeginClient doctorFeginClient;


    private final PatientFeginClient patientFeginClient;
    /**
     * 消息编/解码对象
     */
    private final IMessageTransCoder messageTransCoder;

    public DoctorMessageHandler(MessageDao messageDao, PushHandler pushHandler, ClientStatusHandler clientHandler, SessionHandler sessionHandler,
                                @Qualifier(value = "doctorTransfer") DoctorTransfer messageTransfer, MediaFileConfig mediaConfig,
                                DoctorFeginClient doctorFeginClient, PatientFeginClient patientFeginClient, IMessageTransCoder messageTransCoder,
                                PatientInquirerRepository patientInquirerRepository) {
        this.messageDao = messageDao;
        this.pushHandler = pushHandler;
        this.clientHandler = clientHandler;
        this.sessionHandler = sessionHandler;
        this.messageTransfer = messageTransfer;
        this.mediaConfig = mediaConfig;
        this.doctorFeginClient = doctorFeginClient;
        this.patientFeginClient = patientFeginClient;
        this.messageTransCoder = messageTransCoder;
        this.patientInquirerRepository = patientInquirerRepository;
    }

    /**
     * 消息处理
     */
    @Override
    protected Message<?> handleMessage(Message<?> message) throws NaiteruiIMException {

        // 准备消息发送
        message = this.preparedMessage(message);
        // 保存消息之前,清空session信息
        Session session = message.getSession();
        //message.setSession(null);

        // 已认证的医生进行处方合规处理
        boolean drugFlag = false;

        // 持久化消息
        this.persistentMessage(message);

        boolean isDrugType = Constants.MESSAGE_TYPE_DRUG == message.getType();
        boolean isRecordType = Constants.MESSAGE_TYPE_DOCTORS_MEDICALRECORD == message.getType();
        boolean isFollowUpType = ImConstants.MESSAGE_TYPE_FOLLOW_UP == message.getType();
        // 是否是医生发送文章消息
        boolean isSendArticle = ImConstants.MESSAGE_TYPE_DOCTOR_SEND_ARTICLE == message.getType();
        // 是否是医生加号消息
        boolean isPlusSign = ImConstants.MESSAGE_TYPE_DOCTOR_PLUS_SIGN == message.getType();
        if (isDrugType || isRecordType || isFollowUpType || isSendArticle || isPlusSign) {
            if (Message.MESSAGE_VISUAL_TYPE_ALL != message.getVisualType()) {
                drugFlag = true;
            }
            // 图文问诊推送的处方、病例或者随访，同步给医生
            boolean syncDoctor =
                (isDrugType && ((RecommandDrug)message.getContent()).getRecomType() == RecommandDrug.RECOM_TYPE_MANNUL)
                    || (isRecordType && ((DoctorsMedicalRecord)message.getContent())
                        .getConsultType() == CommonConstant.ConsultType.GRAPHIC)
                    || isFollowUpType || isSendArticle || isPlusSign;
            // 发送文章、加号消息同步给医生
            if (syncDoctor) {
                this.sendSyncMessage(message);
            } else {
                // 如果不同步医生的处方或病例，设置消息可见级别为患者可见
                message.setVisualType(Message.MESSAGE_VISUAL_TYPE_PATIENT);
                messageDao.updateVisualType(message.getId(), Message.MESSAGE_VISUAL_TYPE_PATIENT);
            }
        }
        // 重新保存session信息
        message.setSession(session);
        // 持久化后是否推送
        if (drugFlag) {
            return message;
        }

        // 发送消息
        this.sendMessage(message);

        // 消息发送后执行一些其他逻辑操作
        this.afterSendMessage(message);

        return message;
    }

    /**
     * 消息发送前的准备工作
     */
    @Override
    public Message<?> preparedMessage(Message<?> message) throws NaiteruiIMException {

        super.preparedMessage(message);

        // 处理消息
        int type = message.getType();

        // 多媒体消息
        if (type == Constants.MESSAGE_TYPE_MEDIA_AUDIO || type == Constants.MESSAGE_TYPE_MEDIA_IMG || type == Constants.MESSAGE_TYPE_MEDIA_VIDEO) {
            // 转换为多媒体消息内容对象
            Media media = (Media) message.getContent();
            // 截取掉域名
            media.setPath(media.getPath().replaceAll(mediaConfig.getServerDomain(), ""));
        }

        return message;
    }

    /**
     * 持久化从医生端发送给患者端的消息内容
     */
    @Override
    public void persistentMessage(Message<?> message) throws NaiteruiIMException {
        super.persistentMessage(message);
        this.messageDao.saveMessage(message);
    }

    /**
     * 发送消息至患者端
     */
    @Override
    public void sendMessage(Message<?> message) throws NaiteruiIMException {

        /*
         * 获取消息接收者的当前在线状态
         * 如果用户不在线：保存离线消息，推送push通知
         * 如果用户在线：直接向用户推送消息
         */
        // 处理发送消息用户id信息
        /*PatientInquirer patientInquirer = patientInquirerRepository.findByInquirerId(message.getTo().getId());
        Long userId = patientInquirer != null ? patientInquirer.getPatientId() : message.getTo().getId();
        boolean isOnline = this.clientHandler.isOnline(NameUtils.ptMsgTopic(userId));*/
        boolean isOnline = this.clientHandler.isOnline(NameUtils.ptMsgTopic(message.getTo().getId()));
//        boolean usePtApp = RedisUtil.keyOps().existsKey(CommonConstant.PT_APP_LOGIN_IN + message.getTo().getId());
        LOGGER.info("向患者端发送消息前的状态检查，isOnline:{}", isOnline);
//        LOGGER.info("发送消息前的状态检查，isOnline:{}, usePtApp:{}", isOnline, usePtApp);
        // 如果不在线则保存离线消息并发送push消息
        if (!isOnline) {
//        if (!isOnline && usePtApp) {
            // 不保存服务评价通知消息
            if (message.getType() != Constants.MESSAGE_TYPE_PATIENTS_SERVICE_EVALUATION
                    || message.getType() != Constants.MESSAGE_TYPE_PATIENTS_DISEASE
                    || message.getType() != Constants.MESSAGE_TYPE_PATIENTS_CONSULT_FINISH
                    || message.getType() != Constants.MESSAGE_TYPE_PATIENTS_FIRST_BIND) {
                // 保存离线消息
                this.persistentOffineMessage(message);
            }

            // 推送离线消息push通知, 推送离线消息通知，要排除用户已经登出的情况,如果离线推送小程序订阅消息
            if (this.clientHandler.canPush(NameUtils.ptMsgTopic(message.getTo().getId()))) {
                try {
                    LOGGER.info("发送小程序订阅消息，message:{}", message);
                    this.publishMaMessage(message);
//                    this.pushHandler.pushOfflineMsgNotice(Constants.PUSH_USER_TYPE_PATIENT, message.getTo().getId(), message);
                } catch (Exception e) {
                    LOGGER.error("push offline message notice failure, exception is {}", e.getMessage(), e);
                }
            }
        }
        // 发送消息 该处用于处理将消息发送至微信端&APP端口
        this.messageTransfer.sendMessage(message, isOnline);
    }

    /**
     * 消息发送之后的一些附加操作
     */
    @Override
    public void afterSendMessage(Message<?> message) {
        // 是否来自医生端
        boolean isFromDct = message.getRelation() == Constants.MESSAGE_RESOURCE_DOCTOR;
        Long doctorId = isFromDct ? message.getFrom().getId() : message.getTo().getId();
        Long patientId = isFromDct ? message.getTo().getId() : message.getFrom().getId();
        // 如果为医生发送，则需要设置会话状态
        if (isFromDct && message.getSysMsgType() == Message.SYS_MSG_TYPE_CONSUMER) {
            if(message.getSession() != null && !StringUtils.isEmpty(message.getSession().getSessionId())){
                ThreadPoolUtil.execute(() -> {
                    this.setConsultSessionStatus(message.getSession().getSessionId(), doctorId, patientId);
                });
            }
        }

        // 消息所在会话拓展属性更新
        if (message.getSession() != null && (Constants.MESSAGE_TYPE_DRUG == message.getType()
                || Constants.MESSAGE_TYPE_DOCTORS_MEDICALRECORD == message.getType())) {

            Integer containRecord = Constants.MESSAGE_TYPE_DOCTORS_MEDICALRECORD == message.getType() ? SessionDetails.CONTAIN_RECORD_YES : null;
            Integer containRecom = Constants.MESSAGE_TYPE_DRUG == message.getType() && message.getVisualType() == Message.MESSAGE_VISUAL_TYPE_ALL
                    ? SessionDetails.CONTAIN_RECOM_YES : null;
            this.sessionHandler.updateContains(message.getSession().getSessionId(), containRecom, containRecord);
        }
    }

    /**
     * 设置会话状态
     *
     * @param sessionId
     * @param doctorId
     * @param patientId
     */
    private void setConsultSessionStatus(String sessionId, Long doctorId, Long patientId){
        boolean hexist = RedisUtil.hashOps().hexist(CommonConstant.CONSULT_DOCTOR_SEND_MESSAGE_CACHE, sessionId);
        if (hexist) {
            return;
        }
        // 修改会话状态为已接诊
        doctorFeginClient.updateSessionStatus(sessionId, 2);
        RedisUtil.hashOps().hset(CommonConstant.CONSULT_DOCTOR_SEND_MESSAGE_CACHE, sessionId, doctorId + "_" + patientId);
    }

    /********************************** 私有方法 **************************************/
    /**
     * 保存离线消息
     */
    private void persistentOffineMessage(Message<?> message) {
        // 保存离线消息
        this.messageDao.saveOfflineMessage(message);
    }

    /**
     * 同步消息到医生端
     */
    public void sendSyncMessage(Message<?> message) {
        // 获取消息接收者的当前在线状态
        boolean isOnline = this.clientHandler.isOnline(NameUtils.drMsgTopic(message.getFrom().getId()));
        LOGGER.info("同步消息至医生端，isOnline：{}， message：{}", isOnline, JSONUtil.toJsonStr(message));
        // 如果不在线则保存多平台同步离线消息
        if (!isOnline) {
            // 保存患者端微信自发送离线消息
            this.persistentSyncOffineMessage(message);
        } else {
            this.messageTransfer.sendSyncOfflineMessage(message);
        }
    }

    /**
     * 保存离线的 同步消息
     */
    private void persistentSyncOffineMessage(Message<?> message) {
        // 保存离线消息
        this.messageDao.saveSyncOfflineMessage(message);
    }

    /**
     * 消息前置校验
     */
    @Override
    public void preValidate(Message<?> message) {
    }

    /**
     * 发布小程序订阅消息
     *
     * @param message
     */
    private void publishMaMessage(Message<?> message) throws NaiteruiIMException {
        int msgType = 0;
        switch (message.getType()) {
            // 文本消息
            case Constants.MESSAGE_TYPE_TEXT:
                msgType = MsgMaPublishParamBO.MsgType.TEXT;
                break;
            // 图片消息
            case Constants.MESSAGE_TYPE_MEDIA_IMG:
                msgType = MsgMaPublishParamBO.MsgType.IMAGE;
                break;
            // 音频消息
            case Constants.MESSAGE_TYPE_MEDIA_AUDIO:
                msgType = MsgMaPublishParamBO.MsgType.VOICE;
                break;
            // 视频消息
            case Constants.MESSAGE_TYPE_MEDIA_VIDEO:
                msgType = MsgMaPublishParamBO.MsgType.VIDEO;
                break;
            // 推荐用药
            case Constants.MESSAGE_TYPE_DRUG:
                msgType = MsgMaPublishParamBO.MsgType.RECOMMEND;
                break;
            case Constants.MESSAGE_TYPE_DOCTORS_MEDICALRECORD:
                msgType = MsgMaPublishParamBO.MsgType.MEDICALRECORD;
                break;
            case Constants.MESSAGE_TYPE_PATIENTS_SERVICE_EVALUATION:
                msgType = MsgMaPublishParamBO.MsgType.SERVICE_EVALUATION;
                break;
            case Constants.MESSAGE_TYPE_REMIND_PATIENTS_FILL_MEDICAL_RECORD:
            case Constants.MESSAGE_TYPE_PATIENTS_LAST_MEDICAL_RECORD:
            case ImConstants.MESSAGE_TYPE_PATIENTS_FIRST_BIND:
            case ImConstants.MESSAGE_TYPE_FIRST_BIND_UNNAMED:
            case ImConstants.MESSAGE_TYPE_INQUIRER_INFO_PERFECT:
            case ImConstants.MESSAGE_TYPE_FOLLOW_UP_FILLIN:
            case ImConstants.MESSAGE_TYPE_FOLLOW_UP:
                return;
                //msgType = MsgMaPublishParamBO.MsgType.FOLLOW_UP_NOT_STARTED;
                //break;
            default:
                msgType = MsgMaPublishParamBO.MsgType.TEXT;
                break;
        }
        LOGGER.info("拼接消息：{}", msgType);
        patientFeginClient.publishMaMessage(MsgMaPublishParamBO.builder()
                .doctorId(message.getFrom().getId())
                .patientId(message.getTo().getId())
                .msgType(msgType)
                .content(JSONUtil.toJsonStr(message.getContent()))
                .build());
        LOGGER.debug("push message {} to doctor {}", JSONUtil.toJsonStr(message), message.getFrom().getId());
    }
}
